﻿using OpenTK;
using OpenTK.Audio;
using OpenTK.Audio.OpenAL;
using System;
using System.IO;

namespace Gargoyle.Roton
{
    public class AudioOpenAL : Audio
    {
        private AudioContext audioContext;
        private int buffer;
        private int source;

        public AudioOpenAL(int newSampleRate, BitDepth newBitsPerSample, int newBufferSize)
            : base(newSampleRate, newBitsPerSample, newBufferSize)
        {
            audioContext = new AudioContext(null, newSampleRate);
            source = AL.GenSource();

            base.BufferFull += AudioOpenAL_BufferFull;
        }

        ~AudioOpenAL()
        {
            base.BufferFull -= AudioOpenAL_BufferFull;

            AL.DeleteSource(source);
            AL.DeleteBuffer(buffer);

            audioContext.Dispose();
        }

        public void AudioOpenAL_BufferFull(object sender, BufferFullArgs e)
        {
            int bufferLength = e.Buffer.Capacity;
            byte[] soundData = new byte[bufferLength];
            int BuffersProcessed;
            int[] BuffersToFree;

            if(e.Buffer.Length == 0) return;

            e.Buffer.Read(soundData, 0, bufferLength);
            buffer = AL.GenBuffer();
            
            if(AL.GetError() == ALError.IllegalCommand)
            {
                // immediately recreate the source and buffer if they are ever made invalid
                AL.DeleteSource(source);
                AL.DeleteBuffer(buffer);

                source = AL.GenSource();
                buffer = AL.GenBuffer();
            }

            AL.BufferData(buffer, ALFormat.Mono16, soundData, bufferLength, SampleRate);
            AL.SourceQueueBuffer(source, buffer);
            ALSourceState state = AL.GetSourceState(source);
            if(AL.GetSourceState(source) != ALSourceState.Playing)
                AL.SourcePlay(source);

            AL.GetSource(source, ALGetSourcei.BuffersProcessed, out BuffersProcessed);
            if(BuffersProcessed == 0)
                return;
            BuffersToFree = AL.SourceUnqueueBuffers(source, BuffersProcessed);
            AL.DeleteBuffers(BuffersToFree);
        }

        protected override void StopStream()
        {
            int[] BuffersToFree;
            int BuffersQueued;

            base.StopStream();

            // stop playing audio, then recreate the source and buffer
            AL.SourceStop(source);
            AL.GetSource(source, ALGetSourcei.BuffersQueued, out BuffersQueued);

            if(BuffersQueued > 0)
            {
                BuffersToFree = AL.SourceUnqueueBuffers(source, BuffersQueued);
                AL.DeleteBuffers(BuffersToFree);
            }

            AL.DeleteBuffer(buffer);
            AL.DeleteSource(source);

            source = AL.GenSource();
            buffer = AL.GenBuffer();
        }
    }
}